package com.codefalse.mybatis.gen.plugin;

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.List;

/**
 * @author songxiangfeng
 * Created At 2021-02-18 17:08
 */
public class ListByParamsPlugin extends PluginAdapter {
    private static final String LIST_BY_PARAMS = "listByParams";

    private FullyQualifiedJavaType entityJavaType;
    private FullyQualifiedJavaType mapperJavaType;
    private FullyQualifiedJavaType whereDynamicJavaType;

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        entityJavaType = topLevelClass.getType();
        whereDynamicJavaType = new FullyQualifiedJavaType(entityJavaType.getPackageName() + ".WhereDynamic");
        return true;
    }

    @Override
    public boolean clientGenerated(Interface interfaze, IntrospectedTable introspectedTable) {

        mapperJavaType = interfaze.getType();

        interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
        interfaze.addImportedType(new FullyQualifiedJavaType("java.util.List"));
        interfaze.addImportedType(whereDynamicJavaType);

        FullyQualifiedJavaType listJavaType = new FullyQualifiedJavaType("java.util.List");
        listJavaType.addTypeArgument(entityJavaType);

        interfaze.addMethod(generateClientMethod(LIST_BY_PARAMS,
                listJavaType,
                new Parameter(
                        whereDynamicJavaType,
                        "dynamicParams",
                        "@Param(\"dynamicParams\")"
                ),
                new Parameter(
                        new FullyQualifiedJavaType(entityJavaType.getShortName() + ".Column"),
                        "columns",
                        "@Param(\"columns\")",
                        true
                )));

        interfaze.addMethod(generateClientMethod("selectByParams",
                entityJavaType,
                new Parameter(
                        whereDynamicJavaType,
                        "dynamicParams",
                        "@Param(\"dynamicParams\")"
                ),
                new Parameter(
                        new FullyQualifiedJavaType(entityJavaType.getShortName() + ".Column"),
                        "columns",
                        "@Param(\"columns\")",
                        true
                )));
        interfaze.addMethod(generateClientMethod("deleteByParams",
                new FullyQualifiedJavaType("int"),
                new Parameter(
                        whereDynamicJavaType,
                        "dynamicParams",
                        "@Param(\"dynamicParams\")"
                )));
        return true;
    }

    private Method generateClientMethod(String methodName, FullyQualifiedJavaType returnType, Parameter... parameters) {
        Method method = new Method(methodName);
        method.setAbstract(true);
        method.setReturnType(returnType);
        for (Parameter parameter : parameters) {
            method.addParameter(parameter);
        }
        return method;
    }

    private void dynamicColumnDocument(XmlElement rootElement, String allColumn) {
        XmlElement sqlElement = new XmlElement("sql");
        sqlElement.addAttribute(new Attribute("id", "dynamic_column"));

        XmlElement columnChooseElement = new XmlElement("choose");
        XmlElement columnExistElement = new XmlElement("when");
        columnExistElement.addAttribute(new Attribute("test", "columns != null and columns.length > 0"));
        XmlElement columnForeachElement = new XmlElement("foreach");
        columnForeachElement.addAttribute(new Attribute("collection", "columns"));
        columnForeachElement.addAttribute(new Attribute("item", "col"));
        columnForeachElement.addAttribute(new Attribute("separator", ","));
        columnForeachElement.addElement(new TextElement("${col.actualName}"));
        columnExistElement.addElement(columnForeachElement);
        columnChooseElement.addElement(columnExistElement);
        XmlElement columnOtherElement = new XmlElement("otherwise");
        columnOtherElement.addElement(new TextElement(allColumn));
        columnChooseElement.addElement(columnOtherElement);

        sqlElement.addElement(columnChooseElement);
        rootElement.addElement(sqlElement);
    }

    private void dynamicWhereDocument(XmlElement rootElement) {
        XmlElement sqlElement = new XmlElement("sql");
        sqlElement.addAttribute(new Attribute("id", "dynamic_where"));

        XmlElement paramExistElement = new XmlElement("if");
        paramExistElement.addAttribute(new Attribute("test", "list != null and !list.isEmpty()"));
        XmlElement whereElement = new XmlElement("where");
        XmlElement paramForeachElement = new XmlElement("foreach");
        paramForeachElement.addAttribute(new Attribute("collection", "list"));
        paramForeachElement.addAttribute(new Attribute("item", "whereItem"));
        paramForeachElement.addElement(new TextElement("AND ${whereItem.column} <![CDATA[ ${whereItem.operator} ]]>"));
        XmlElement operatorChooseElement = new XmlElement("choose");
        XmlElement operatorWhenElement = new XmlElement("when");
        operatorWhenElement.addAttribute(new Attribute("test", "whereItem.operator == 'in' or whereItem.operator == 'not in'"));
        XmlElement subForeachElement = new XmlElement("foreach");
        subForeachElement.addAttribute(new Attribute("collection", "whereItem.value"));
        subForeachElement.addAttribute(new Attribute("item", "inItem"));
        subForeachElement.addAttribute(new Attribute("open", "("));
        subForeachElement.addAttribute(new Attribute("separator", ","));
        subForeachElement.addAttribute(new Attribute("close", ")"));
        subForeachElement.addElement(new TextElement("#{inItem}"));
        operatorWhenElement.addElement(subForeachElement);
        operatorChooseElement.addElement(operatorWhenElement);
        //默认问本值
        XmlElement operatorOtherElement = new XmlElement("otherwise");
        operatorOtherElement.addElement(new TextElement("#{whereItem.value}"));
        operatorChooseElement.addElement(operatorOtherElement);
        paramForeachElement.addElement(operatorChooseElement);
        whereElement.addElement(paramForeachElement);
        paramExistElement.addElement(whereElement);
        sqlElement.addElement(paramExistElement);

        rootElement.addElement(sqlElement);
    }

    private void dynamicOrderDocument(XmlElement rootElement) {
        XmlElement sqlElement = new XmlElement("sql");
        sqlElement.addAttribute(new Attribute("id", "dynamic_order"));
        XmlElement orderExistElement = new XmlElement("if");
        orderExistElement.addAttribute(new Attribute("test", "sorts != null and !sorts.isEmpty()"));
        orderExistElement.addElement(new TextElement("ORDER BY "));
        XmlElement orderForeachElement = new XmlElement("foreach");
        orderForeachElement.addAttribute(new Attribute("collection", "sorts"));
        orderForeachElement.addAttribute(new Attribute("item", "s"));
        orderForeachElement.addAttribute(new Attribute("separator", ","));
        orderForeachElement.addElement(new TextElement("${s}"));
        orderExistElement.addElement(orderForeachElement);
        sqlElement.addElement(orderExistElement);
        rootElement.addElement(sqlElement);
    }

    private XmlElement generateSelectDocument(String methodName, String mapperPackage, String tableName) {

        XmlElement selectElement = new XmlElement("select");
        selectElement.addAttribute(new Attribute("id", methodName));
        selectElement.addAttribute(new Attribute("resultMap", "BaseResultMap"));
        selectElement.addElement(new TextElement("SELECT"));
        XmlElement dynamicColumnElement = new XmlElement("include");
        dynamicColumnElement.addAttribute(new Attribute("refid", "dynamic_column"));
        selectElement.addElement(dynamicColumnElement);
        //from
        selectElement.addElement(new TextElement("FROM `" + tableName + "`"));


        if (methodName.startsWith("select")) {
            XmlElement dynamicWhereElement = new XmlElement("include");
            dynamicWhereElement.addAttribute(new Attribute("refid", mapperPackage + ".CommonMapper.dynamic_where_sort"));
            selectElement.addElement(dynamicWhereElement);
            selectElement.addElement(new TextElement("limit 1"));
        } else {
            XmlElement dynamicWhereElement = new XmlElement("include");
            dynamicWhereElement.addAttribute(new Attribute("refid", mapperPackage + ".CommonMapper.dynamic_where_sort_limit"));
            selectElement.addElement(dynamicWhereElement);
        }
        return selectElement;
    }

    private XmlElement generateDeleteDocument(String methodName, String tableName, String mapperPackage) {
        XmlElement deleteElement = new XmlElement("delete");
        deleteElement.addAttribute(new Attribute("id", methodName));
        deleteElement.addAttribute(new Attribute("parameterType", "java.lang.Integer"));
        deleteElement.addElement(new TextElement("delete from `" + tableName + "`"));
        XmlElement dynamicWhereElement = new XmlElement("include");
        dynamicWhereElement.addAttribute(new Attribute("refid", mapperPackage + ".CommonMapper.dynamic_where"));
        deleteElement.addElement(dynamicWhereElement);
        return deleteElement;
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        StringBuilder sb = new StringBuilder();
        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        for (IntrospectedColumn column : primaryKeyColumns) {
            sb.append("`").append(column.getActualColumnName()).append("`").append(",");
        }
        List<IntrospectedColumn> introspectedColumns = introspectedTable.getBaseColumns();
        for (IntrospectedColumn column : introspectedColumns) {
            sb.append("`").append(column.getActualColumnName()).append("`").append(",");
        }
        sb.deleteCharAt(sb.length() - 1);
        dynamicColumnDocument(document.getRootElement(), sb.toString());

        XmlElement rootElement = document.getRootElement();
        String mapperPackage = mapperJavaType.getPackageName();
        String tableName = introspectedTable.getTableConfiguration().getTableName();
        rootElement.addElement(generateSelectDocument(LIST_BY_PARAMS, mapperPackage, tableName));
        rootElement.addElement(generateSelectDocument("selectByParams", mapperPackage, tableName));

        rootElement.addElement(generateDeleteDocument("deleteByParams", tableName, mapperPackage));
        return true;
    }
}
